// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections;

namespace Google.OrTools.ConstraintSolver
{
/**
 * This class acts as a intermediate step between a c++ decision builder and a
 * .Net one. Its main purpose is to catch the .Net application exception
 * launched when a failure occurs during the Next() call, and to return
 * silently a System.ApplicationException that will propagate the failure back
 * to the C++ code.
 *
 */
public class NetDecisionBuilder : DecisionBuilder
{
    /**
     * This methods wraps the calls to next() and catches fail exceptions.
     * It currently catches all application exceptions.
     */
    public override Decision NextWrapper(Solver solver)
    {
        try
        {
            return Next(solver);
        }
        catch (ApplicationException /*e*/)
        {
            // TODO(user): Catch only fail exceptions.
            return solver.MakeFailDecision();
        }
    }
    /**
     * This is the new method to subclass when defining a .Net decision builder.
     */
    public virtual Decision Next(Solver solver)
    {
        return null;
    }
}

/**
 * This class acts as a intermediate step between a c++ decision and a
 * .Net one. Its main purpose is to catch the .Net application
 * exception launched when a failure occurs during the
 * Apply()/Refute() calls, and to set the ShouldFail() flag on the
 * solver that will propagate the failure back to the C++ code.
 *
 */
public class NetDecision : Decision
{
    /**
     * This methods wraps the calls to Apply() and catches fail exceptions.
     * It currently catches all application exceptions.
     */
    public override void ApplyWrapper(Solver solver)
    {
        try
        {
            Apply(solver);
        }
        catch (ApplicationException /*e*/)
        {
            // TODO(user): Catch only fail exceptions.
            solver.ShouldFail();
        }
    }
    /**
     * This is a new method to subclass when defining a .Net decision.
     */
    public virtual void Apply(Solver solver)
    {
        // By default, do nothing
    }

    public override void RefuteWrapper(Solver solver)
    {
        try
        {
            Refute(solver);
        }
        catch (ApplicationException /*e*/)
        {
            // TODO(user): Catch only fail exceptions.
            solver.ShouldFail();
        }
    }
    /**
     * This is a new method to subclass when defining a .Net decision.
     */
    public virtual void Refute(Solver solver)
    {
    }
}

public class NetDemon : Demon
{
    /**
     * This methods wraps the calls to next() and catches fail exceptions.
     */
    public override void RunWrapper(Solver solver)
    {
        try
        {
            Run(solver);
        }
        catch (ApplicationException /*e*/)
        {
            // TODO(user): Check that this is indeed a fail. Try implementing
            // custom exceptions (hard).
            solver.ShouldFail();
        }
    }
    /**
     * This is the new method to subclass when defining a .Net decision builder.
     */
    public virtual void Run(Solver solver)
    {
    }
    public override int Priority()
    {
        return Solver.NORMAL_PRIORITY;
    }
    public override string ToString()
    {
        return "NetDemon";
    }
}

public class NetConstraint : Constraint
{
    public NetConstraint(Solver s) : base(s)
    {
    }

    public override void InitialPropagateWrapper()
    {
        try
        {
            InitialPropagate();
        }
        catch (ApplicationException /*e*/)
        {
            solver().ShouldFail();
        }
    }
    public virtual void InitialPropagate()
    {
    }
    public override string ToString()
    {
        return "NetConstraint";
    }
}

public class IntVarEnumerator : IEnumerator
{
    private IntVarIterator iterator_;

    // Enumerators are positioned before the first element
    // until the first MoveNext() call.
    private bool first_ = true;

    public IntVarEnumerator(IntVarIterator iterator)
    {
        iterator_ = iterator;
    }

    public bool MoveNext()
    {
        if (first_)
        {
            iterator_.Init();
            first_ = false;
        }
        else
        {
            iterator_.Next();
        }
        return iterator_.Ok();
    }

    public void Reset()
    {
        first_ = true;
    }

    object IEnumerator.Current
    {
        get {
            return Current;
        }
    }

    public long Current
    {
        get {
            if (!first_ && iterator_.Ok())
            {
                return iterator_.Value();
            }
            else
            {
                throw new InvalidOperationException();
            }
        }
    }
}

public partial class IntVarIterator : BaseObject, IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        return (IEnumerator)GetEnumerator();
    }

    public IntVarEnumerator GetEnumerator()
    {
        return new IntVarEnumerator(this);
    }
}
} // namespace Google.OrTools.ConstraintSolver
